/*
 * Copyright (c) 2021 Eshel.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package cn.android.basic.components

import com.google.gson.Gson
import java.lang.reflect.ParameterizedType
import java.lang.reflect.Type
import java.util.*

/**
 * <br>createBy Gw
 * <br>createTime: 2021/6/24 11:19
 * 用于描述泛型, 方便替代TypeToken进行gson解析
 *
 * @see ParameterizedTypeJavaImpl
 * 示例代码如下
 */

private fun examples(){
    //Map<String, String>
    ParameterizedTypeImpl.from(Map::class.java)
        .argument(String::class.java)
        .argument(String::class.java)
        .build()

    //Map<String, Map<Map<String, String>, List<String>>>
    val type: Type = ParameterizedTypeImpl.from(Map::class.java)
        .argument(String::class.java)
        .argument(Map::class.java) {
            argument(Map::class.java, String::class.java, String::class.java)
            argument(List::class.java, String::class.java)
        }.build()

    val gson = Gson()
    val result:Map<String, Map<Map<String, String>, List<String>>> = gson.fromJson("{xxx}", type)
}

class ParameterizedTypeImpl(
    private val ownerType: Type?,
    private val rawType: Class<*>,
    private val actualTypeArguments: Array<out Type>
) : ParameterizedType {

    companion object {

        @JvmStatic
        fun from(rawType: Class<*>): BuilderImpl {
            return BuilderImpl(rawType)
        }

        @JvmStatic
        fun from(rawType: Class<*>, ownerType: Type? = null): BuilderImpl {
            return BuilderImpl(ownerType, rawType)
        }
    }

    override fun getActualTypeArguments(): Array<out Type> {
        return actualTypeArguments
    }

    override fun getRawType(): Type {
        return rawType
    }

    override fun getOwnerType(): Type? {
        return ownerType
    }

    override fun hashCode(): Int {
        return ((ownerType?.hashCode() ?: 0)
                xor actualTypeArguments.contentHashCode()
                xor rawType.hashCode())
    }

    override fun equals(other: Any?): Boolean {
        if (other !is ParameterizedType) {
            return false
        }

        return (getRawType() == other.rawType
                && getOwnerType() == other.ownerType
                && Arrays.equals(getActualTypeArguments(), other.actualTypeArguments))
    }

    override fun toString(): String {
        val sb = StringBuilder()
        sb.append(toString(rawType))
        if (actualTypeArguments.isNotEmpty()) {
            sb.append('<')
            for (i in actualTypeArguments.indices) {
                if (i != 0) sb.append(", ")
                sb.append(toString(actualTypeArguments[i]))
            }
            sb.append('>')
        }
        return sb.toString()
    }

    private fun toString(type: Type): String {
        return if (type is Class<*>) type.simpleName else type.toString()
    }

    open class Builder {
        private var owner: Builder? = null
        private var rawType: Class<*>
        private val actualTypeArguments: MutableList<Any> = ArrayList()
        private var ownerType: Type? = null

        constructor(rawType: Class<*>) {
            this.rawType = rawType
        }

        constructor(ownerType: Type?, rawType: Class<*>) {
            this.ownerType = ownerType
            this.rawType = rawType
        }

        open fun argument(argumentRawType: Class<*>, subArguments: Builder.() -> Any): Builder {
            val builder = Builder(argumentRawType)
            builder.owner = this
            subArguments.invoke(builder)
            actualTypeArguments.add(builder)
            return this
        }

        open fun argument(argumentRawType: Class<*>): Builder {
            actualTypeArguments.add(argumentRawType)
            return this
        }

        open fun argument(argumentRawType: Class<*>, vararg subArgument: Class<*>): Builder {
            actualTypeArguments.add(
                ParameterizedTypeImpl(
                    ownerRawType,
                    argumentRawType,
                    subArgument
                )
            )
            return this
        }

        val ownerRawType: Type? get() = owner?.rawType ?: ownerType

        fun getActualTypeArguments(): Array<out Type> {
            val args: MutableList<Type> = ArrayList(actualTypeArguments.size)
            for (argument in actualTypeArguments) {
                when (argument) {
                    is Builder -> {
                        args.add(
                            ParameterizedTypeImpl(
                                argument.ownerRawType,
                                argument.rawType,
                                argument.getActualTypeArguments()
                            )
                        )
                    }
                    is ParameterizedTypeJavaImpl -> {
                        args.add(argument as Type)
                    }
                    else -> {
                        args.add(ParameterizedTypeImpl(rawType, argument as Class<*>, emptyArray()))
                    }
                }
            }
            return args.toTypedArray()
        }

        protected open fun build(): ParameterizedTypeImpl {
            return if (actualTypeArguments.isEmpty()) {
                ParameterizedTypeImpl(ownerRawType, rawType, emptyArray())
            } else ParameterizedTypeImpl(ownerRawType, rawType, getActualTypeArguments())
        }
    }

    class BuilderImpl : Builder {
        constructor(rawType: Class<*>) : super(rawType)
        constructor(ownerType: Type?, rawType: Class<*>) : super(ownerType, rawType)

        override fun argument(argumentRawType: Class<*>): BuilderImpl {
            return super.argument(argumentRawType) as BuilderImpl
        }

        override fun argument(argumentRawType: Class<*>, subArguments: Builder.() -> Any): BuilderImpl {
            return super.argument(argumentRawType, subArguments) as BuilderImpl
        }

        override fun argument(argumentRawType: Class<*>, vararg subArgument: Class<*>): BuilderImpl {
            return super.argument(argumentRawType, *subArgument) as BuilderImpl
        }

        public override fun build(): ParameterizedTypeImpl {
            return super.build()
        }
    }
}